"""
Base Runner Interface

Defines the abstract interface for executing solver frameworks.
"""

from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Any, Union
from pathlib import Path
import subprocess


class ExecutionResult:
    """Result of a solver execution."""
    
    def __init__(self, success: bool, return_code: int = 0, stdout: str = "", 
                 stderr: str = "", execution_time: Optional[float] = None):
        self.success = success
        self.return_code = return_code
        self.stdout = stdout
        self.stderr = stderr
        self.execution_time = execution_time
        
    def __bool__(self) -> bool:
        return self.success


class BaseRunner(ABC):
    """
    Abstract base class for framework runners.
    
    Runners handle execution of built solver frameworks with various configurations.
    """
    
    def __init__(self, extension_name: str):
        """
        Initialize the runner.
        
        Args:
            extension_name: Name of the extension this runner belongs to
        """
        self.extension_name = extension_name
        
    @abstractmethod
    def can_run(self, executable_path: Path) -> bool:
        """
        Check if this runner can execute the given binary.
        
        Args:
            executable_path: Path to executable
            
        Returns:
            True if runner can handle this executable
        """
        pass
    
    @abstractmethod
    def run(self, executable_path: Path, config_file: Optional[Path] = None,
            run_config: Optional[Dict[str, Any]] = None, 
            timeout: Optional[int] = None) -> ExecutionResult:
        """
        Execute the solver framework.
        
        Args:
            executable_path: Path to solver executable
            config_file: Path to solver configuration file
            run_config: Runtime configuration parameters
            timeout: Execution timeout in seconds
            
        Returns:
            ExecutionResult with execution details
        """
        pass
    
    @abstractmethod
    def get_command_line(self, executable_path: Path, config_file: Optional[Path] = None,
                        run_config: Optional[Dict[str, Any]] = None) -> List[str]:
        """
        Get the command line for executing the solver.
        
        Args:
            executable_path: Path to solver executable
            config_file: Path to solver configuration file  
            run_config: Runtime configuration parameters
            
        Returns:
            Command line as list of strings
        """
        pass
    
    def validate_environment(self) -> List[str]:
        """
        Validate that the runtime environment is suitable.
        
        Returns:
            List of error messages (empty if valid)
        """
        return []
    
    def get_run_info(self, executable_path: Path) -> Dict[str, Any]:
        """
        Get information about the executable and runtime.
        
        Args:
            executable_path: Path to executable
            
        Returns:
            Dictionary with runtime information
        """
        return {
            'runner': self.__class__.__name__,
            'extension': self.extension_name,
            'executable': str(executable_path),
        }
    
    def prepare_environment(self, run_config: Optional[Dict[str, Any]] = None) -> Dict[str, str]:
        """
        Prepare environment variables for execution.
        
        Args:
            run_config: Runtime configuration parameters
            
        Returns:
            Dictionary of environment variables
        """
        return {}
    
    def get_default_config(self) -> Dict[str, Any]:
        """
        Get default runtime configuration.
        
        Returns:
            Default configuration dictionary
        """
        return {}
    
    def validate_config(self, config: Dict[str, Any]) -> List[str]:
        """
        Validate runtime configuration.
        
        Args:
            config: Runtime configuration to validate
            
        Returns:
            List of validation error messages (empty if valid)
        """
        return []
    
    def estimate_runtime(self, config_file: Optional[Path] = None,
                        run_config: Optional[Dict[str, Any]] = None) -> Optional[int]:
        """
        Estimate execution time in seconds.
        
        Args:
            config_file: Path to solver configuration file
            run_config: Runtime configuration parameters
            
        Returns:
            Estimated runtime in seconds, or None if unknown
        """
        return None
    
    def supports_parallel(self) -> bool:
        """
        Check if this runner supports parallel execution.
        
        Returns:
            True if parallel execution is supported
        """
        return False
    
    def get_parallel_info(self) -> Dict[str, Any]:
        """
        Get information about parallel execution capabilities.
        
        Returns:
            Dictionary with parallel execution info
        """
        return {
            'supported': self.supports_parallel(),
            'max_processes': 1,
            'mpi_supported': False,
        }
    
    def kill_process(self, process: subprocess.Popen) -> bool:
        """
        Kill a running solver process.
        
        Args:
            process: Process to kill
            
        Returns:
            True if process was killed successfully
        """
        try:
            process.terminate()
            process.wait(timeout=5)
            return True
        except (subprocess.TimeoutExpired, ProcessLookupError):
            try:
                process.kill()
                process.wait(timeout=2)
                return True
            except (subprocess.TimeoutExpired, ProcessLookupError):
                return False
    
    def __str__(self) -> str:
        """String representation of the runner."""
        return f"{self.__class__.__name__}({self.extension_name})"
        
    def __repr__(self) -> str:
        """Detailed string representation."""
        return f"<{self.__class__.__name__}(extension='{self.extension_name}')>"